package net.da.tools;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.da.processor.IOProcessor;
import net.da.processor.IORequest;
import net.da.processor.IOResponse;
import net.da.processor.ProcessorProvider;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class GwtIoServiceTool {

	private static final String SERVER = "server";

	private static final String SERVICE = "service";

	private static final String CLIENT = "client";

	private static final String GTO = "gto";

	private static final String ASYNC_SUFFIX = "Async";

	private static final char POINT = '.';

	private static final String CLIENT_SERVICE = CLIENT + POINT + SERVICE;

	private static final String CLIENT_GTO = CLIENT + POINT + GTO;

	private static final String PROCESS_REQUEST = "processRequest";

	public static void main(String... args) {

		System.out
				.print(GwtIoServiceToolTest.class.getName() + " started ....");
		GwtIoServiceTool instance = new GwtIoServiceTool();
		BuilderConfig config = extractConfigFromArgs(args);
		instance.buildGwtIOServiceAPI(config);
		System.out.println("Ok!");
	}

	static BuilderConfig extractConfigFromArgs(String[] args) {
		// TODO:ALB
		throw new UnsupportedOperationException();
	}

	void buildGwtIOServiceAPI(BuilderConfig config) {

		validateConfig(config);
		if (config.isResultJar()) {
			buildResultJar(config);
		} else {
			buildResultFiles(config);
		}
	}

	void validateConfig(BuilderConfig config) {
		// TODO:ALB (improve)
	}

	void buildResultFiles(BuilderConfig config) {

		String destFolder = config.getDestFolder();
		String resultRootPackage = config.getResultRootPackage();
		PackageStructure packageStructure = buildPackageStructure(config);
		List<ClassDescriptor> classes = packageStructure
				.getClasses(resultRootPackage);
		for (ClassDescriptor descriptor : classes) {
			createJavaFile(descriptor, destFolder);
		}
	}

	void createJavaFile(ClassDescriptor descriptor, String folder) {

		JavaFileCreator creator = new JavaFileCreator();
		creator.setClassDescriptor(descriptor);
		creator.setFolder(folder);
		creator.createJavaFile();
	}

	void buildResultJar(BuilderConfig config) {
		// TODO:ALB
		throw new UnsupportedOperationException();
	}

	List<String> extractJavaFiles(String source) {

		List<String> files = new ArrayList<String>();
		File file = new File(source);
		if (file.isDirectory()) {
			String[] dirFiles = file.list();
			for (int i = 0; i < dirFiles.length; i++) {
				String name = source + '/' + dirFiles[i];
				file = new File(name);
				if (file.isDirectory()) {
					List<String> list = extractJavaFiles(name);
					files.addAll(list);
				} else if (isJavaFile(name)) {
					files.add(name);
				}
			}
		}
		return files;
	}

	@SuppressWarnings("unchecked")
	List<Class<? extends IOProcessor>> extractProcessorClasses(
			List<String> javaFiles) {

		List<Class<? extends IOProcessor>> clazzes = new ArrayList<Class<? extends IOProcessor>>();
		if (javaFiles != null) {
			ClassLoader loader = ClassLoader.getSystemClassLoader();
			for (String fileName : javaFiles) {
				String className = extractFullClassNameFromPath(fileName);
				try {
					Class<?> clazz = loader.loadClass(className);
					if (isProcesser(clazz)) {
						clazzes.add((Class<? extends IOProcessor>) clazz);
					}
				} catch (ClassNotFoundException e) {
					throw new RuntimeException(e);
				}
			}
		}
		return clazzes;
	}

	boolean isProcesser(Class<?> clazz) {

		Class[] interfaces = clazz.getInterfaces();
		List<Class> list = Arrays.asList(interfaces);
		if (list.contains(IOProcessor.class)) {
			return true;
		}

		Type superClazz = clazz.getGenericSuperclass();
		if (superClazz != null) {
			interfaces = superClazz.getClass().getInterfaces();
			list = Arrays.asList(interfaces);
			if (list.contains(IOProcessor.class)) {
				return true;
			}
		}
		return false;
	}

	String extractFullClassNameFromPath(String fileName) {

		Pattern pattern = Pattern.compile("(\\/{1})(\\w+)(\\.{1}java)");
		Matcher matcher = pattern.matcher(fileName);
		matcher.find();
		String className = matcher.group(2);
		String content = readFile(fileName);
		pattern = Pattern.compile("(^\\s*package\\s+)([a-z0-9.]+)(\\s*;$)",
				Pattern.MULTILINE);
		matcher = pattern.matcher(content);
		String packageName = "";
		if (matcher.find()) {
			packageName = matcher.group(2);
			packageName += '.';
		}
		return packageName + className;
	}

	String readFile(String fileName) {

		StringBuffer buffer = new StringBuffer();
		try {
			FileReader reader = new FileReader(fileName);
			BufferedReader br = new BufferedReader(reader);
			String line = br.readLine();
			while (line != null) {
				buffer.append(line + '\n');
				line = br.readLine();
			}

		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return buffer.toString();

	}

	boolean isJavaFile(String fileName) {
		return fileName != null && fileName.endsWith(".java");
	}

	PackageStructure buildPackageStructure(BuilderConfig config) {

		PackageStructure stucture = new PackageStructure();
		List<Class<? extends IOProcessor>> processors = extractProcessorClasses(config);
		for (Class<? extends IOProcessor> processor : processors) {
			stucture = buildServiceInterfaces(config, processor, stucture);
		}
		stucture = buildRemoteServletObject(config, stucture);
		return stucture;
	}

	List<Class> extractDtoClasses(Class<? extends IOProcessor> processor) {

		List<Class> list = new ArrayList<Class>();
		Method method = findProcessRequestMethod(processor);
		Class<?> returnType = method.getReturnType();
		Class<?>[] parameters = method.getParameterTypes();
		list.add(returnType);
		list.add(parameters[0]);
		return list;
	}

	Method findProcessRequestMethod(Class<? extends IOProcessor> processor) {

		String methodName = "processRequest";
		Method[] methods = processor.getMethods();
		boolean isFound = false;
		Method method = null;
		for (int i = 0; i < methods.length && !isFound; i++) {
			method = methods[i];
			isFound = methodName.equals(method.getName());
		}
		return method;
	}

	PackageStructure buildServiceInterfaces(BuilderConfig config,
			Class<? extends IOProcessor> processor, PackageStructure structure) {

		describeClass(processor, structure);

		String rootPackage = config.getResultRootPackage();
		String serviceInterfaceName = buildServiceInterfaceName(processor);
		String serviceAsyncInterfaceName = serviceInterfaceName + ASYNC_SUFFIX;
		String servicePackage = rootPackage + POINT + CLIENT_SERVICE;

		Class<? extends IORequest> requestDtoClass = extractRequestDtoClass(processor);
		ClassDescriptor requestDtoObject = buildGwtDtoObject(rootPackage,
				serviceInterfaceName, requestDtoClass, structure);
		Class<? extends IOResponse> responseDtoClass = extractResponseDtoClass(processor);
		ClassDescriptor responseDtoObject = buildGwtDtoObject(rootPackage,
				serviceInterfaceName, responseDtoClass, structure);
		structure.addClass(requestDtoObject);
		structure.addClass(responseDtoObject);

		String processorName = processor.getSimpleName();
		{// service interface
			ClassDescriptor serviceInterface;
			ClassDescriptor gwtRemoteServiceInterface = describeGwtRemoteServiceInterface(structure);
			String fullName = servicePackage + '.' + serviceInterfaceName;
			if (structure.containsClass(fullName)) {
				serviceInterface = structure.findClass(fullName);
			} else {
				serviceInterface = new ClassDescriptor();
				serviceInterface.setAccessQuilifier("public");
				serviceInterface.setPackageName(servicePackage);
				serviceInterface.setShortName(serviceInterfaceName);
				serviceInterface.addInterface(gwtRemoteServiceInterface);
				serviceInterface.setInterfaceType(true);
				structure.addClass(serviceInterface);
			}
			MethodDescriptor serviceMethod = buildGwtServiceMethod(
					processorName, requestDtoObject, responseDtoObject);
			serviceInterface.addMethod(serviceMethod);
		}

		{// async service interface
			ClassDescriptor asyncServiceInterface;
			String fullName = servicePackage + '.' + serviceAsyncInterfaceName;
			if (structure.containsClass(fullName)) {
				asyncServiceInterface = structure.findClass(fullName);
			} else {
				asyncServiceInterface = new ClassDescriptor();
				asyncServiceInterface.setAccessQuilifier("public");
				asyncServiceInterface.setPackageName(servicePackage);
				asyncServiceInterface.setShortName(serviceAsyncInterfaceName);
				asyncServiceInterface.setInterfaceType(true);
				structure.addClass(asyncServiceInterface);
			}

			ClassDescriptor callBackObject = describeClass(AsyncCallback.class,
					ClassDescriptor.LOWEST_LEVEL, structure);
			MethodDescriptor asyncServiceMethod = buildGwtAsyncServiceMethod(
					processorName, requestDtoObject, callBackObject);
			asyncServiceInterface.addMethod(asyncServiceMethod);
		}
		return structure;
	}

	PackageStructure buildRemoteServletObject(BuilderConfig config,
			PackageStructure stucture) {
		String name = "RemoteServiceServletImpl";

		List<ClassDescriptor> services = extractServices(stucture);
		ClassDescriptor gwtRemoteServlet = describeGwtRemoteServlet(stucture);
		ClassDescriptor processorProvider = describeProcessorProvider(stucture);
		FieldDescriptor field = buildProcessorProviderField(processorProvider);
		MethodDescriptor contructor = buildRemoteServletConstructor(name, field);
		String packageName = buildRemoteServletPackage(config);

		ClassDescriptor servletDescriptor = new ClassDescriptor();
		servletDescriptor.setAccessQuilifier("public");
		servletDescriptor.setShortName(name);
		servletDescriptor.setSuperType(gwtRemoteServlet);
		servletDescriptor.setPackageName(packageName);
		servletDescriptor.addField(field);
		servletDescriptor.addMethod(contructor);

		for (ClassDescriptor service : services) {
			servletDescriptor.addInterface(service);
			List<MethodDescriptor> serviceMethods = implementServiceMethods(
					service, stucture);
			for (MethodDescriptor serviceMethod : serviceMethods) {
				servletDescriptor.addMethod(serviceMethod);
			}
		}
		stucture.addClass(servletDescriptor);
		return stucture;
	}

	List<MethodDescriptor> implementServiceMethods(ClassDescriptor service,
			PackageStructure stucture) {

		List<MethodDescriptor> implementedMethods = new ArrayList<MethodDescriptor>();
		List<MethodDescriptor> serviceMethods = service.getMethods();
		for (MethodDescriptor serviceMethod : serviceMethods) {
			MethodDescriptor implementedMethod = implementServiceMethod(
					service, serviceMethod, stucture);
			implementedMethods.add(implementedMethod);
		}
		return implementedMethods;
	}

	MethodDescriptor implementServiceMethod(ClassDescriptor service,
			MethodDescriptor serviceMethod, PackageStructure stucture) {

		MethodDescriptor implementdMethod;
		try {
			implementdMethod = (MethodDescriptor) serviceMethod.clone();
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
		MethodBody body = buildServiceMethodBody(service, serviceMethod,
				stucture);
		implementdMethod.setBody(body);
		return implementdMethod;
	}

	MethodBody buildServiceMethodBody(ClassDescriptor service,
			MethodDescriptor serviceMethod, PackageStructure structure) {

		// example
		// A01Processor processor = factory.getA01Processor();
		// A01Request request = new A01Request();
		// request.setField01(gwtRequest.getField01());
		// request.setField02(gwtRequest.getField02());
		// A01Response response = processor.processRequest(request);
		// A01GwtResponse gwtResponse = new A01GwtResponse();
		// gwtResponse.setField01(response.getField01());
		// gwtResponse.setField02(response.getField02());
		// return gwtResponse;

		ClassDescriptor processor = findBackendProcessor(service,
				serviceMethod, structure);

		ClassDescriptor responseDto = serviceMethod.getReturnType()
				.getClassDescriptor();
		ClassDescriptor backendResponseDto = findBackendResponseDto(processor);
//		ClassDescriptor requestDto = serviceMethod.getParams().get(0).getType()
//				.getClassDescriptor();
		ClassDescriptor backendRequestDto = findBackendRequestDto(processor);

		MethodBody body = new MethodBody();
				
		BodyLine part0 = buildBodyPartInitProcessorIdentifier("processor",
				processor, structure);
		BodyLine part1 = buildBodyPartInitIdentifierByNewObject("ioRequest",
				backendRequestDto);
		List<BodyLine> part2 = buildBodyPartInitObjectBySetters("ioRequest", "request",
				backendRequestDto);
		BodyLine part3 = buildBodyPartInvokeProccesRequest("ioResponse",
				backendResponseDto, "processor", "ioRequest");
		BodyLine part4 = buildBodyPartInitIdentifierByNewObject("response",
				responseDto);
		List<BodyLine> part5 = buildBodyPartInitObjectBySetters("response",
				"ioResponse", responseDto);
		BodyLine part6 = buildBodyPartReturn("response");

		body.addElement(part0);
		body.addElement(part1);
		for (BodyLine line : part2) {
			body.addElement(line);
		}		
		body.addElement(part3);
		body.addElement(part4);
		for (BodyLine line : part5) {
			body.addElement(line);
		}
		body.addElement(part6);

		return body;
	}

	private MethodDescriptor findProcessRequestMethod(ClassDescriptor processor) {

		// TODO:ALB (imporve)
		List<MethodDescriptor> all = processor.getMethods();
		MethodDescriptor required = null;
		boolean isFound = false;
		for (Iterator<MethodDescriptor> iter = all.iterator(); iter.hasNext()
				&& !isFound;) {
			MethodDescriptor method = iter.next();
			if (method.isHasReturnType()
					&& !method.getReturnType().isPrimitive()
					&& !method.getReturnType().getClassDescriptor()
							.isInterfaceType()
					&& method.getName().equals("processRequest")) {
				isFound = true;
				required = method;
			}
		}
		return required;
	}

	private ClassDescriptor findBackendRequestDto(ClassDescriptor processor) {

		MethodDescriptor required = findProcessRequestMethod(processor);
		return required.getParams().get(0).getType().getClassDescriptor();
	}

	private ClassDescriptor findBackendResponseDto(ClassDescriptor processor) {

		MethodDescriptor required = findProcessRequestMethod(processor);
		return required.getReturnType().getClassDescriptor();
	}

	BodyLine buildBodyPartReturn(String indentifier) {
		
		BodyLine line = new BodyLine();
		line.setLine("return " + indentifier + ";");		
		return line;		
	}

	BodyLine buildBodyPartInvokeProccesRequest(String returnIndentifier,
			ClassDescriptor returnType, String processorIndentifier,
			String paramName) {

		String returnTypeName = returnType.getShortName();
		StringBuffer buffer = new StringBuffer();
		buffer.append(returnTypeName).append(' ').append(returnIndentifier);
		buffer.append(" = ").append(processorIndentifier).append(POINT).append(
				PROCESS_REQUEST);
		buffer.append('(').append(paramName).append(')').append(';');
		BodyLine line = new BodyLine();
		line.addUsage(returnType);
		line.setLine(buffer.toString());		
		return line;
	}

	List<BodyLine> buildBodyPartInitObjectBySetters(String destIndentifier,
			String srcIndentifier, ClassDescriptor destType) {

		List<MethodDescriptor> setters = extractSetters(destType);
		List<BodyLine> lines = new ArrayList<BodyLine>();
				
		for (MethodDescriptor setter : setters) {
			StringBuffer buffer = new StringBuffer();
			String setterName = setter.getName();
			String getterName = buildGetterNameFromSetter(setterName);
			buffer.append(destIndentifier).append('.');
			buffer.append(setterName).append('(');
			buffer.append(srcIndentifier).append('.');
			buffer.append(getterName).append("())");
			buffer.append(';');
			BodyLine line = new BodyLine();
			line.setLine(buffer.toString());
			lines.add(line);			
		}				
		return lines;
	}

	String buildGetterNameFromSetter(String setterName) {
		return setterName.replaceFirst("set", "get");
	}

	List<MethodDescriptor> extractSetters(ClassDescriptor cd) {

		List<MethodDescriptor> setters = new ArrayList<MethodDescriptor>();
		List<MethodDescriptor> methods = cd.getMethods();
		for (MethodDescriptor method : methods) {
			String methodName = method.getName();
			if (methodName.startsWith("set")) {
				setters.add(method);
			}
		}
		return setters;
	}

	BodyLine buildBodyPartInitIdentifierByNewObject(String identifier,
			ClassDescriptor type) {

		String typeName = type.getShortName();
		StringBuffer buffer = new StringBuffer();
		buffer.append(typeName).append(' ').append(identifier);
		buffer.append(" = ").append("new ").append(typeName).append("()");
		buffer.append(';');				
		BodyLine line = new BodyLine();
		line.setLine(buffer.toString());
		line.addUsage(type);
		return line;
	}

	BodyLine buildBodyPartInitProcessorIdentifier(String identifier,
			ClassDescriptor processor, PackageStructure stucture) {

		ClassDescriptor processorProvider = describeProcessorProvider(stucture);
		String processorProviderName = processorProvider.getShortName();
		processorProviderName = uncapitalize(processorProviderName);

		String typeName = processor.getShortName();
		StringBuffer buffer = new StringBuffer();
		buffer.append(typeName).append(' ').append(identifier);
		buffer.append(" = ").append(processorProviderName).append(".get")
				.append(typeName).append("()");
		buffer.append(';');
		
		BodyLine line = new BodyLine();
		line.addUsage(processor);		
		line.setLine(buffer.toString());		
		return line;
	}

	ClassDescriptor findBackendProcessor(ClassDescriptor service,
			MethodDescriptor serviceMethod, PackageStructure structure) {
		// TODO:ALB (improve)
		List<ClassDescriptor> all = findBackendPocessors(structure);
		String methodName = serviceMethod.getName();
		methodName = methodName.toUpperCase();
		ClassDescriptor backendObject = null;
		boolean isFound = false;
		for (Iterator<ClassDescriptor> iter = all.iterator(); iter.hasNext()
				&& !isFound;) {
			ClassDescriptor clazz = iter.next();
			if (clazz.getShortName().toUpperCase().contains(methodName)) {
				backendObject = clazz;
				isFound = true;
			}
		}
		return backendObject;
	}

	// ClassDescriptor findBackendDto(ClassDescriptor dto,
	// PackageStructure structure) {
	//
	// // TODO:ALB (improve)
	// List<ClassDescriptor> all = findBackendDtos(structure);
	//
	// String shortName0 = dto.getShortName();
	// if (shortName0.contains("GwtRequest")) {
	// int index = shortName0.indexOf("GwtRequest");
	// shortName0 = shortName0.substring(0, index) + "reuest";
	// } else if (shortName0.contains("GwtResponse")) {
	// int index = shortName0.indexOf("GwtResponse");
	// shortName0 = shortName0.substring(0, index) + "response";
	// } else {
	// throw new RuntimeException("Descriptor is not pertain to gwtDto!");
	// }
	//
	// shortName0 = shortName0.toUpperCase();
	// ClassDescriptor backendObject = null;
	// boolean isFound = false;
	// for (Iterator<ClassDescriptor> iter = all.iterator(); iter.hasNext()
	// && !isFound;) {
	// ClassDescriptor clazz = iter.next();
	// if (clazz.getShortName().toUpperCase().contains(shortName0)) {
	// backendObject = clazz;
	// isFound = true;
	// }
	// }
	// return backendObject;
	// }

	private List<ClassDescriptor> findBackendPocessors(
			PackageStructure structure) {

		ClassDescriptor interfaze = describeClass(IOProcessor.class, structure);
		List<ClassDescriptor> processors = findByInterface(structure, interfaze);
		return processors;
	}

	private List<ClassDescriptor> findByInterface(PackageStructure structure,
			ClassDescriptor interfaze) {

		List<ClassDescriptor> all = structure.getAllClasses();
		List<ClassDescriptor> required = new ArrayList<ClassDescriptor>();

		for (Iterator<ClassDescriptor> iter = all.iterator(); iter.hasNext();) {
			ClassDescriptor clazz = iter.next();
			if (isClassImplementInterface(clazz, interfaze)) {
				required.add(clazz);
			}
		}
		return required;
	}

	// private List<ClassDescriptor> findBackendDtos(PackageStructure structure)
	// {
	//
	// ClassDescriptor interfaze0 = describeClass(IORequest.class, structure);
	// ClassDescriptor interfaze1 = describeClass(IOResponse.class, structure);
	// List<ClassDescriptor> dtos0 = findByInterface(structure, interfaze0);
	// List<ClassDescriptor> dtos1 = findByInterface(structure, interfaze1);
	// List<ClassDescriptor> allDtos = new ArrayList<ClassDescriptor>();
	// allDtos.addAll(dtos0);
	// allDtos.addAll(dtos1);
	// return allDtos;
	// }

	MethodDescriptor buildRemoteServletConstructor(String name,
			FieldDescriptor field) {

		MethodDescriptor md = new MethodDescriptor();
		String strLine = field.getName() + " = " + field.getType().getName()
				+ ".getInstance();";

		MethodBody body = new MethodBody();
		BodyLine bodyLine = new BodyLine();
		bodyLine.setLine(strLine);
		body.addElement(bodyLine);

		md.setAccessQualifier("public");
		md.setName(name);
		md.setConstructor(true);
		md.setBody(body);
		return md;
	}

	FieldDescriptor buildProcessorProviderField(ClassDescriptor processorFactory) {

		TypeDescriptor td = buildTypeDescriptor(processorFactory);
		String name = processorFactory.getShortName();
		name = uncapitalize(name);
		FieldDescriptor fd = buildFieldDescriptor(name, td);
		return fd;
	}

	ClassDescriptor describeProcessorProvider(PackageStructure stucture) {

		ClassDescriptor descriptor = describeClass(ProcessorProvider.class,
				stucture);
		return descriptor;
	}

	String buildRemoteServletPackage(BuilderConfig config) {

		String packageName = config.getResultRootPackage();
		packageName += POINT + SERVER;
		return packageName;
	}

	ClassDescriptor describeGwtRemoteServlet(PackageStructure stucture) {

		ClassDescriptor descriptor = describeClass(RemoteServiceServlet.class,
				ClassDescriptor.LOWEST_LEVEL, stucture);
		return descriptor;
	}

	List<ClassDescriptor> extractServices(PackageStructure stucture) {

		List<ClassDescriptor> services = new ArrayList<ClassDescriptor>();
		List<ClassDescriptor> all = stucture.getAllClasses();
		for (ClassDescriptor descriptor : all) {
			if (isService(descriptor, stucture)) {
				services.add(descriptor);
			}
		}
		return services;
	}

	boolean isService(ClassDescriptor descriptor, PackageStructure stucture) {

		// TODO (improve) search must be deeper
		List<ClassDescriptor> interfaces = descriptor.getInterfaces();
		ClassDescriptor gwtInterface = describeGwtRemoteServiceInterface(stucture);

		boolean isFound = false;
		for (Iterator<ClassDescriptor> iter = interfaces.iterator(); iter
				.hasNext()
				&& !isFound;) {
			ClassDescriptor interfaze = iter.next();
			isFound = gwtInterface.equals(interfaze);
		}
		return isFound;
	}

	MethodDescriptor buildGwtAsyncServiceMethod(String processorName,
			ClassDescriptor requestDtoObject, ClassDescriptor callBackObject) {

		String serviceMethodName = buildServiceMethodName(processorName);
		TypeDescriptor returnType = buildVoidType();
		Parameter param0 = buildParameter(requestDtoObject, "request");
		Parameter param1 = buildParameter(callBackObject, "callback");

		MethodDescriptor md = new MethodDescriptor();
		md.setAccessQualifier("public");
		md.setReturnType(returnType);
		md.setName(serviceMethodName);
		md.addParam(param0);
		md.addParam(param1);
		return md;
	}

	MethodDescriptor buildGwtServiceMethod(String processorName,
			ClassDescriptor requestDtoObject, ClassDescriptor responseDtoObject) {

		String serviceMethodName = buildServiceMethodName(processorName);
		TypeDescriptor returnType = buildTypeDescriptor(responseDtoObject);
		Parameter param0 = buildParameter(requestDtoObject, "request");

		MethodDescriptor md = new MethodDescriptor();
		md.setAccessQualifier("public");
		md.setReturnType(returnType);
		md.setName(serviceMethodName);
		md.addParam(param0);
		return md;
	}

	String buildServiceMethodName(String processorName) {

		Pattern pattern = Pattern.compile("^(\\S+)(Processor)$");
		Matcher matcher = pattern.matcher(processorName);
		matcher.find();
		String methodName = matcher.group(1);
		methodName = uncapitalize(methodName);
		return methodName;
	}

	ClassDescriptor buildGwtDtoObject(String rootPackage,
			String serviceClassName, Class<?> dtoClass,
			PackageStructure structure) {

		ClassDescriptor gwtIsSerializableInterface = buildGwtIsSerializableInterface();
		String dtoPackage = rootPackage + POINT + CLIENT_GTO + POINT
				+ serviceClassName.toLowerCase();
		String name = buildDtoObjectName(dtoClass);

		ClassDescriptor dtoObject = new ClassDescriptor();
		dtoObject.setAccessQuilifier("public");
		dtoObject.setPackageName(dtoPackage);
		dtoObject.setShortName(name);
		dtoObject.addInterface(gwtIsSerializableInterface);

		List<Field> fields = extractBeanFields(dtoClass);
		for (Field field : fields) {
			FieldDescriptor fieldDescr = describeField(field, structure);
			MethodDescriptor setter = buildSetter(fieldDescr);
			MethodDescriptor getter = buildGetter(fieldDescr);
			dtoObject.addField(fieldDescr);
			dtoObject.addMethod(setter);
			dtoObject.addMethod(getter);
		}
		return dtoObject;
	}

	String buildDtoObjectName(Class<?> dtoClass) {

		boolean isRequestDto;
		if (isClassImplementInterface(dtoClass, IORequest.class)) {
			isRequestDto = true;
		} else if (isClassImplementInterface(dtoClass, IOResponse.class)) {
			isRequestDto = false;
		} else {
			throw new IllegalArgumentException(
					"Class must implement IORequest or IOResponse!");
		}

		String className = dtoClass.getSimpleName();
		Pattern pattern = Pattern.compile("^(\\S+)(Request|Response)$");
		Matcher matcher = pattern.matcher(className);
		if (matcher.find()) {
			className = matcher.group(1);
		}
		return className + (isRequestDto ? "GwtRequest" : "GwtResponse");
	}

	boolean isClassImplementInterface(Class<?> dtoClass, Class<?> interfaze) {

		// TODO:ALB (improve) search mustbe depper super class etc
		Class[] interfaces = dtoClass.getInterfaces();
		boolean isFound = false;
		for (int i = 0; i < interfaces.length && !isFound; i++) {
			isFound = interfaces[i].equals(interfaze);
		}
		return isFound;
	}

	boolean isClassImplementInterface(ClassDescriptor clazz,
			ClassDescriptor interfaze) {

		// TODO:ALB (improve) search mustbe depper super class etc
		List<ClassDescriptor> interfaces = clazz.getInterfaces();
		boolean isFound = false;
		for (Iterator<ClassDescriptor> iter = interfaces.iterator(); iter
				.hasNext();) {
			ClassDescriptor interfaze0 = iter.next();
			isFound = interfaze0.equals(interfaze);
		}
		return isFound;
	}

	MethodDescriptor buildGetter(FieldDescriptor fieldDescr) {

		MethodDescriptor md = new MethodDescriptor();
		String fieldName = fieldDescr.getName();
		String methodName = buildGetterName(fieldName);

		TypeDescriptor returnType = fieldDescr.getType();
		String strLine = "return " + fieldName + ';';
		
		MethodBody body = new MethodBody();
		BodyLine bodyLine = new BodyLine();
		bodyLine.setLine(strLine);
		body.addElement(bodyLine);

		md.setAbstractMrthod(false);
		md.setAccessQualifier("public");
		md.setReturnType(returnType);
		md.setName(methodName);
		md.setBody(body);
		return md;
	}

	MethodDescriptor buildSetter(FieldDescriptor fieldDescr) {

		MethodDescriptor md = new MethodDescriptor();
		String fieldName = fieldDescr.getName();
		String methodName = buildSetterName(fieldName);
		TypeDescriptor fieldType = fieldDescr.getType();

		TypeDescriptor returnType = buildVoidType();
		Parameter param0 = new Parameter();
		param0.setName(fieldName);
		param0.setType(fieldType);
		String strLine = "this." + fieldName + " = " + fieldName + ';';
		
		MethodBody body = new MethodBody();
		BodyLine bodyLine = new BodyLine();
		bodyLine.setLine(strLine);
		body.addElement(bodyLine);

		md.setAbstractMrthod(false);
		md.setAccessQualifier("public");
		md.setReturnType(returnType);
		md.setName(methodName);
		md.setBody(body);
		md.addParam(param0);
		return md;
	}

	String buildGetterName(String fieldName) {

		String getterName = "get" + capitalize(fieldName);
		return getterName;
	}

	String buildSetterName(String fieldName) {

		String getterName = "set" + capitalize(fieldName);
		return getterName;
	}

	TypeDescriptor buildVoidType() {

		TypeDescriptor td = new TypeDescriptor();
		td.setName("void");
		td.setPrimitive(true);
		return td;
	}

	ClassDescriptor buildGwtIsSerializableInterface() {
		// TODO:ALB (improve)
		ClassDescriptor descriptor = new ClassDescriptor();
		descriptor.setInterfaceType(true);
		descriptor.setPackageName(IsSerializable.class.getPackage().getName());
		descriptor.setAccessQuilifier("public");
		descriptor.setShortName(IsSerializable.class.getSimpleName());
		return descriptor;
	}

	ClassDescriptor describeClass(Class<?> clazz, PackageStructure structure) {

		String fullName = clazz.getName();
		boolean isSystemClass = fullName.startsWith("java")
				|| fullName.startsWith("javax");
		int level = isSystemClass ? ClassDescriptor.LOWEST_LEVEL
				: ClassDescriptor.HIGHEST_LEVEL;
		return describeClass(clazz, level, structure);
	}

	ClassDescriptor describeClass(Class<?> clazz, int descriptionLevel,
			PackageStructure structure) {

		// TODO:ALB (improve)
		String fullName = clazz.getName();
		ClassDescriptor descriptor;
		if (structure.containsClass(fullName)) {
			descriptor = structure.findClass(fullName);
		} else {
			descriptor = new ClassDescriptor();
			String packageName = clazz.getPackage().getName();
			String shortName = clazz.getSimpleName();
			boolean isInterface = clazz.isInterface();
			Class<?> superClazz = clazz.getSuperclass();
			Class[] interfaces = clazz.getInterfaces();
			Field[] fields = clazz.getDeclaredFields();
			Method[] methods = clazz.getDeclaredMethods();

			descriptor.setAccessQuilifier("public");
			descriptor.setPackageName(packageName);
			descriptor.setShortName(shortName);
			descriptor.setInterfaceType(isInterface);

			structure.addClass(descriptor);

			if (descriptionLevel == ClassDescriptor.HIGHEST_LEVEL) {
				if (superClazz != null && !superClazz.equals(Object.class)) {
					ClassDescriptor superDescriptor = describeClass(superClazz,
							structure);
					descriptor.setSuperType(superDescriptor);
				}
				for (int i = 0; i < interfaces.length; i++) {
					ClassDescriptor interfaze = describeClass(interfaces[i],
							structure);
					descriptor.addInterface(interfaze);
				}
				for (int i = 0; i < fields.length; i++) {
					FieldDescriptor fd = describeField(fields[i], structure);
					descriptor.addField(fd);
				}
				for (int i = 0; i < methods.length; i++) {
					MethodDescriptor fd = describeMethod(methods[i], structure);
					descriptor.addMethod(fd);
				}
			}
		}
		return descriptor;
	}

	MethodDescriptor describeMethod(Method method, PackageStructure structure) {

		MethodDescriptor md = new MethodDescriptor();
		String methodName = method.getName();
		Class<?> returnType = method.getReturnType();
		Class<?>[] params = method.getParameterTypes();

		TypeDescriptor returnTd = buildTypeDescriptor(returnType, structure);
		md.setAccessQualifier("public");
		md.setName(methodName);
		md.setReturnType(returnTd);
		for (int i = 0; i < params.length; i++) {
			Parameter param = buildParameter(params[i], "p" + i, structure);
			md.addParam(param);
		}
		return md;
	}

	Parameter buildParameter(ClassDescriptor cd, String name) {

		Parameter param = new Parameter();
		TypeDescriptor td = buildTypeDescriptor(cd);
		param.setType(td);
		param.setName(name);
		return param;
	}

	TypeDescriptor buildTypeDescriptor(ClassDescriptor cd) {

		TypeDescriptor td = new TypeDescriptor();
		td.setClassDescriptor(cd);
		return td;
	}

	Parameter buildParameter(Class<?> clazz, String name,
			PackageStructure structure) {

		Parameter param = new Parameter();
		TypeDescriptor td = buildTypeDescriptor(clazz, structure);
		param.setType(td);
		param.setName(name);
		return param;
	}

	TypeDescriptor buildTypeDescriptor(Class<?> clazz,
			PackageStructure structure) {

		String simpleName = clazz.getSimpleName();
		TypeDescriptor td = new TypeDescriptor();
		if (simpleName.equals("void") || clazz.isPrimitive()) {
			td.setPrimitive(true);
			td.setName(simpleName);
		} else {
			td.setPrimitive(false);
			ClassDescriptor cd = describeClass(clazz, structure);
			td.setClassDescriptor(cd);
		}
		return td;
	}

	FieldDescriptor describeField(Field field, PackageStructure structure) {

		TypeDescriptor type = new TypeDescriptor();
		Class<?> fieldType = field.getType();
		if (fieldType.isPrimitive()) {
			type.setPrimitive(true);
			type.setName(fieldType.getName());
		} else {
			type.setPrimitive(false);
			ClassDescriptor classDescriptor = describeClass(fieldType,
					structure);
			type.setClassDescriptor(classDescriptor);
		}
		FieldDescriptor fd = buildFieldDescriptor(field.getName(), type);
		return fd;

	}

	FieldDescriptor buildFieldDescriptor(String name, TypeDescriptor td) {

		FieldDescriptor fd = new FieldDescriptor();
		fd.setAccessQualifier("private");
		fd.setName(name);
		fd.setType(td);
		return fd;
	}

	List<Field> extractBeanFields(Class<?> dtoClass) {

		// TODO:ALB (improve) - find fileds which have setter and getter
		Field[] fields = dtoClass.getDeclaredFields();
		return Arrays.asList(fields);
	}

	ClassDescriptor describeGwtRemoteServiceInterface(PackageStructure structure) {

		return describeClass(RemoteService.class, ClassDescriptor.LOWEST_LEVEL,
				structure);
	}

	@SuppressWarnings("unchecked")
	Class<? extends IOResponse> extractResponseDtoClass(
			Class<? extends IOProcessor> processor) {

		// TODO:ALB (improve) - generic interface should be found, it may be in
		// super class etc.
		ParameterizedType type = (ParameterizedType) processor
				.getGenericInterfaces()[0];
		Class<? extends IOResponse> returnType = (Class<? extends IOResponse>) type
				.getActualTypeArguments()[0];
		return returnType;
	}

	@SuppressWarnings("unchecked")
	Class<? extends IORequest> extractRequestDtoClass(
			Class<? extends IOProcessor> processor) {

		// TODO:ALB (improve) - generic interface should be found, it may be in
		// super class etc.
		ParameterizedType type = (ParameterizedType) processor
				.getGenericInterfaces()[0];
		Class<? extends IORequest> param0 = (Class<? extends IORequest>) type
				.getActualTypeArguments()[1];
		return param0;
	}

	String buildServiceInterfaceName(Class<? extends IOProcessor> processor) {

		String packageName = processor.getPackage().getName();
		Pattern pattern = Pattern.compile("^\\S+processor.(\\S+)$");
		Matcher matcher = pattern.matcher(packageName);
		matcher.find();
		String packageSuffix = matcher.group(1);
		StringBuffer buffer = new StringBuffer();
		if (packageSuffix.contains(".")) {
			String[] parts = packageSuffix.split(".");
			for (int i = 0; i < parts.length; i++) {
				String part = parts[i];
				part = capitalize(part);
				buffer.append(part);
			}
		} else {
			buffer.append(capitalize(packageSuffix));
		}
		buffer.append("Service");
		return buffer.toString();
	}

	String capitalize(String str) {

		if (str.length() > 1) {
			String first = str.substring(0, 1).toUpperCase();
			str = first + str.substring(1);
			return str;
		} else {
			return str.toUpperCase();
		}
	}

	String uncapitalize(String str) {

		if (str.length() > 1) {
			String first = str.substring(0, 1).toLowerCase();
			str = first + str.substring(1);
			return str;
		} else {
			return str.toLowerCase();
		}
	}

	List<Class<? extends IOProcessor>> extractProcessorClasses(
			BuilderConfig config) {

		String source = config.getSourceFolder();
		List<String> javaFiles = extractJavaFiles(source);
		List<Class<? extends IOProcessor>> processors = extractProcessorClasses(javaFiles);
		return processors;
	}

}
